package com.hyuga.servlet;

import com.hyuga.annotation.MyController;
import com.hyuga.annotation.MyRequestMapping;
import com.hyuga.annotation.MyResource;
import com.hyuga.annotation.MyService;
import hyuga.utils.collection.ListUtil;
import hyuga.utils.collection.MapUtil;
import hyuga.utils.string.StringUtil;

import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * springMvc调度servlet
 *
 * @author hyuga
 * @date 2018-06-21 21:33
 */
public class MyDispatcherServlet extends HttpServlet {

    /**
     * 配置文件
     */
    private Properties properties = new Properties();
    /**
     * 扫描properties配置包路径下的所有文件
     * 存储：com.hyuga.annotation.MyController
     */
    private List<String> packageNames = ListUtil.NEW();
    /**
     * instanceMap把MyController和MyService的实体都放入
     */
    private Map<String, Object> instanceMap = MapUtil.NEW();
    /**
     * 映射Mapping放入/test/doTest和method
     */
    private Map<String, Method> handlerMapping = MapUtil.NEW();
    /**
     * 存储/test和controller对象
     */
    private Map<String, Object> controllerMap = MapUtil.NEW();

    @Override
    public void init(ServletConfig config) {
        //1.加载配置文件,需要扫描的包路径
        loadConfig(config.getInitParameter("contextConfigLocation"));

        //2.初始化所有相关联的类,扫描用户设定的包下面所有的类,往classNames中放入：com.hyuga.controller.TestController
        scannerPackageAndInitPackageNames(properties.getProperty("scanPackage"));

        //3.拿到扫描到的类,通过反射机制,实例化,并且放到ioc容器中(k-v  beanName-bean) beanName默认是首字母小写，把testController和install放入ioc map
        iocInstanceAndInstanceMap();

        //4.初始化HandlerMapping(将url和method对应上)，handlerMapping放入url和method，controllerMap放入url和controllerInstall
        initHandlerMapping();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            //处理请求
            doDispatch(req, resp);
        } catch (Exception e) {
            resp.getWriter().write("500!! Server Exception");
        }
    }

    private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (handlerMapping.isEmpty()) {
            return;
        }

        String url = request.getRequestURI();
        String contextPath = request.getContextPath();

        url = url.replace(contextPath, "").replaceAll("/+", "/");

        if (!this.handlerMapping.containsKey(url)) {
            response.getWriter().write("404 NOT FOUND!");
            return;
        }

        Method method = this.handlerMapping.get(url);

        //获取方法的参数列表
        Class<?>[] parameterTypes = method.getParameterTypes();

        //获取请求的参数
        Map<String, String[]> parameterMap = request.getParameterMap();

        //保存参数值
        Object[] paramValues = new Object[parameterTypes.length];

        //方法的参数列表
        for (int i = 0; i < parameterTypes.length; i++) {
            //根据参数名称，做某些处理
            String requestParam = parameterTypes[i].getSimpleName();

            if ("HttpServletRequest".equals(requestParam)) {
                //参数类型已明确，这边强转类型
                paramValues[i] = request;
                continue;
            }
            if ("HttpServletResponse".equals(requestParam)) {
                paramValues[i] = response;
                continue;
            }
            if ("String".equals(requestParam)) {
                for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
                    String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
                    paramValues[i] = value;
                }
            }
        }
        //利用反射机制来调用
        try {
            //为MyResource注入实体对象
            Object objectClass = this.controllerMap.get(url);
            Object objectInstance = ((Class) objectClass.getClass()).newInstance();
            Field[] declaredFields = this.controllerMap.get(url).getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(MyResource.class)) {
                    declaredField.setAccessible(true);
                    declaredField.set(objectInstance, instanceMap.get(declaredField.getName()));
                }
            }
            method.invoke(objectInstance, paramValues);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 项目编译时，application.properties文件会被移动到target/classes
     * this.getClass().getClassLoader().getResourceAsStream(location)
     * /Users/hyuga/intellijProject/gitos/hyugaMvc/target/classes/application.properties
     *
     * @param location properties文件名
     */
    private void loadConfig(String location) {
        if (StringUtil.hasText(location)) {
            location = location.replaceAll("classpath\\*:", "");
        }
        //把web.xml中的contextConfigLocation对应value值的文件加载到流里面
        try (InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(location)) {
            //用Properties文件加载文件里的内容
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void scannerPackageAndInitPackageNames(String packageName) {
        //把所有的.替换成/
        URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        if (null == url) {
            return;
        }
        File dir = new File(url.getFile());
        for (File file : Objects.requireNonNull(dir.listFiles())) {
            if (file.isDirectory()) {
                //递归读取包
                scannerPackageAndInitPackageNames(packageName + "." + file.getName());
            } else {
                String className = packageName + "." + file.getName().replace(".class", "");
                packageNames.add(className);
            }
        }
    }


    private void iocInstanceAndInstanceMap() {
        if (packageNames.isEmpty()) {
            return;
        }
        for (String className : packageNames) {
            try {
                //把类搞出来,反射来实例化(只有加@MyController需要实例化)
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(MyController.class)) {
                    instanceMap.put(toLowerFirstWord(clazz.getSimpleName()), clazz.newInstance());
                } else if (clazz.isAnnotationPresent(MyService.class)) {
                    Object instance = clazz.newInstance();
                    String key = clazz.getAnnotation(MyService.class).value();
                    if (StringUtil.isEmpty(key)) {
                        key = toLowerFirstWord(clazz.getSimpleName()).replaceAll("Impl", "");
                    }
                    instanceMap.put(key, instance);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void initHandlerMapping() {
        if (instanceMap.isEmpty()) {
            return;
        }
        try {
            for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
                Class<?> clazz = entry.getValue().getClass();
                if (!clazz.isAnnotationPresent(MyController.class)) {
                    continue;
                }

                //拼url时,是controller头的url拼上方法上的url isAnnotationPresent是否存在某个注解
                String baseUrl = "";
                if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                    MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
                    baseUrl = annotation.value();
                }
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                        continue;
                    }

                    MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                    String url = annotation.value();

                    url = (baseUrl + "/" + url).replaceAll("/+", "/");
                    if (handlerMapping.containsKey(url)) {
                        System.err.println("===============================> 请求路径不允许重复：" + url);
                        System.exit(0);
                    }
                    handlerMapping.put(url, method);
                    controllerMap.put(url, clazz.newInstance());
                    System.out.println(url + "," + method);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 把字符串的首字母小写
     *
     * @param name name
     * @return str
     */
    private String toLowerFirstWord(String name) {
        char[] charArray = name.toCharArray();
        charArray[0] += 32;
        return String.valueOf(charArray);
    }


}